﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Unity.EditorCoroutines.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
using ZyGame.Editor.Tools;

namespace ZyGame.Editor.CloudBuilder
{
    internal class CloudBuildService : ZyEditorWindow
    {
        private float lastRefershTime = 0f;
        private float refreshIntervalTime = 10;
        private bool isOptions = false;
        private bool isStart = false;
        private bool isBuilding = false;

        private List<BuildData> completions = new List<BuildData>();
        private Dictionary<string, bool> foldouts = new Dictionary<string, bool>();

        private void Update()
        {
            if (Application.isPlaying)
            {
                return;
            }
            if (Time.realtimeSinceStartup - lastRefershTime < refreshIntervalTime)
            {
                return;
            }
            lastRefershTime = Time.realtimeSinceStartup;
            RefreshBuildList();
        }

        protected override void OnDrawToobar()
        {
            if (GUILayout.Button(isStart ? "Stop" : "Start", EditorStyles.toolbarButton))
            {
                isStart = !isStart;
                isBuilding = false;
                if (isStart)
                {
                    RefreshBuildList();
                }
            }
            if (GUILayout.Button("Options", EditorStyles.toolbarButton))
            {
                isOptions = !isOptions;

            }
            GUILayout.FlexibleSpace();
        }

        protected override void OnDrawDataView()
        {
            if (isOptions)
            {
                CloudBuildOptions.instance.OnGUI();
            }
            else
            {
                ShowBuildListView();
            }
        }

        private void ShowBuildListView()
        {
            for (int i = 0; i < completions.Count; i++)
            {
                GUILayout.BeginVertical(EditorStyles.helpBox);
                BuildData buildData = completions[i];
                GUILayout.BeginHorizontal();
                GUILayout.Label("Project Id:", GUILayout.Width(150));
                GUILayout.Label(buildData.pid);
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();
                GUILayout.Label("Task Id:", GUILayout.Width(150));
                GUILayout.Label(buildData.id);
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();
                GUILayout.Label("Bundle Name:", GUILayout.Width(150));
                GUILayout.Label(buildData.bundleName);
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();
                GUILayout.Label("Time:", GUILayout.Width(150));
                GUILayout.Label(buildData.time.ToString("G"));
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();
                GUILayout.Label("State:", GUILayout.Width(150));
                GUILayout.Label(buildData.State.ToString());
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();
                GUILayout.Label("Platform:", GUILayout.Width(150));
                GUILayout.Label(buildData.platform.ToString());
                GUILayout.EndHorizontal();

                if (!foldouts.TryGetValue(buildData.id, out bool state))
                {
                    foldouts.Add(buildData.id, false);
                }
                foldouts[buildData.id] = EditorGUILayout.Foldout(state, "Files");
                if (foldouts[buildData.id])
                {
                    for (int j = 0; j < buildData.files.Count; j++)
                    {
                        GUILayout.Label(buildData.files[j]);
                    }
                }

                GUILayout.EndVertical();
            }
        }

        private void RefreshBuildList()
        {
            if (isStart is false || isBuilding)
            {
                return;
            }

            IEnumerator Running()
            {
                completions.Clear();
                UnityWebRequest request = Api.GetBuildList(EditorUserBuildSettings.activeBuildTarget);

                yield return request.SendWebRequest();
                if (request.isDone is false || request.result is not UnityWebRequest.Result.Success)
                {
                    yield break;
                }
                List<BuildData> buildDatas = Newtonsoft.Json.JsonConvert.DeserializeObject<List<BuildData>>(request.downloadHandler.text);
                if (buildDatas is null || buildDatas.Count is 0)
                {
                    yield break;
                }
                completions.AddRange(buildDatas);
                this.Repaint();
                Build(buildDatas);
            }
            this.StartCoroutine(Running());
        }

        private void Build(List<BuildData> buildDatas)
        {
            isBuilding = true;
            IEnumerator StartingBuilding()
            {
                for (int i = buildDatas.Count - 1; i >= 0; i--)
                {
                    if (buildDatas[i].State != TaskState.Waiting || buildDatas[i].GetBuildTarget() != EditorUserBuildSettings.activeBuildTarget)
                    {
                        buildDatas.Remove(buildDatas[i]);
                        continue;
                    }
                    EditorUtility.DisplayProgressBar("提示", $"正在下载{buildDatas[i].id}资源...", i / (float)buildDatas.Count);
                    yield return DownloadBuildFileList(buildDatas[i], buildDatas);
                }
                yield return new WaitForSeconds(1);
                BuildAssetBundle(buildDatas);
                for (int i = 0; i < buildDatas.Count; i++)
                {
                    UnityWebRequest request = Api.GetCompletionRequest(buildDatas[i]);
                    yield return request.SendWebRequest();
                }
            }
            this.StartCoroutine(StartingBuilding());
        }

        private void BuildAssetBundle(List<BuildData> buildDatas)
        {
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            if (buildDatas.Count == 0)
            {
                isBuilding = false;
                EditorUtility.ClearProgressBar();
                return;
            }

            AssetBundleBuild[] bundleBuilds = new AssetBundleBuild[buildDatas.Count];
            for (int i = 0; i < bundleBuilds.Length; i++)
            {
                bundleBuilds[i].assetBundleName = buildDatas[i].bundleName;
                bundleBuilds[i].assetNames = new string[buildDatas[i].files.Count];
                for (int j = 0; j < buildDatas[i].files.Count; j++)
                {
                    if (buildDatas[i].files[j].StartsWith("Assets"))
                    {
                        bundleBuilds[i].assetNames[j] = buildDatas[i].files[j];
                    }
                    else
                    {
                        bundleBuilds[i].assetNames[j] = $"Assets/{buildDatas[i].pid}/{Path.GetFileName(buildDatas[i].files[j])}";
                    }
                }
            }

            BuildPipeline.BuildAssetBundles(CloudBuildOptions.instance.output, bundleBuilds, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);

            for (int i = 0; i < buildDatas.Count; i++)
            {
                string cos = buildDatas[i].pid + "/" + EditorUserBuildSettings.activeBuildTarget + "/" + bundleBuilds[i].assetBundleName;
                string local = CloudBuildOptions.instance.output + "/" + bundleBuilds[i].assetBundleName;
                if (File.Exists(local) is false)
                {
                    continue;
                }
                COS.PutObject(CloudBuildOptions.instance.bucket, cos, local);
            }

            for (int i = 0; i < buildDatas.Count; i++)
            {
                for (int j = 0; j < buildDatas[i].files.Count; j++)
                {
                    string root = "Assets/" + buildDatas[i].pid;
                    if (Directory.Exists(root))
                    {
                        Directory.Delete(root, true);
                        File.Delete(root + ".meta");
                    }
                }
            }
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            Directory.Delete(CloudBuildOptions.instance.output, true);
            Directory.CreateDirectory(CloudBuildOptions.instance.output);
            EditorUtility.ClearProgressBar();
            isBuilding = false;
        }

        private IEnumerator DownloadBuildFileList(BuildData buildData, List<BuildData> builds)
        {
            for (int j = 0; j < buildData.files.Count; j++)
            {
                UnityWebRequest request = Api.GetFileDownloadRequest(buildData.files[j]);
                yield return request.SendWebRequest();
                try
                {
                    byte[] bytes = request.downloadHandler.data;
                    if (request.isDone is false || request.result is not UnityWebRequest.Result.Success)
                    {
                        Client.Console.WriteLine(request.url, request.error);
                        builds.Remove(buildData);
                        yield break;
                    }
                    string path = "Assets/" + buildData.pid;
                    if (Directory.Exists(path) is false)
                    {
                        Directory.CreateDirectory($"{path}");
                    }
                    path = path + "/" + Path.GetFileName(buildData.files[j]);
                    File.WriteAllBytes(path, bytes);
                    AssetDatabase.SaveAssets();
                    AssetDatabase.Refresh();
                    string extension = Path.GetExtension(path);
                    AssetImporter importer = AssetImporter.GetAtPath(path);
                    if (extension == ".jpg" || extension == ".png" || extension == ".tga")
                    {
                        TextureImporter textureImporter = (TextureImporter)importer;
                        textureImporter.mipmapEnabled = buildData._minimap;
                        textureImporter.alphaIsTransparency = buildData._alpha;
                        textureImporter.textureType = (TextureImporterType)Enum.Parse(typeof(TextureImporterType), buildData._texture);
                        textureImporter.compressionQuality = 100;
                    }
                    else if (extension == ".obj" || extension == ".fbx")
                    {
                        ModelImporter modelImporter = (ModelImporter)importer;
                        modelImporter.importBlendShapes = buildData._bs;
                        modelImporter.importAnimation = buildData._ani;
                        modelImporter.isReadable = buildData._rw;
                    }
                    importer.SaveAndReimport();
                }
                catch (System.Exception e)
                {
                    Client.Console.WriteError(e, request == null, request.downloadHandler == null);
                    builds.Remove(buildData);
                }
            }
        }
    }
}
